#include "listbuffer.hpp"
#include "streamer.hpp"
#include "protocol_all.hpp"
#include "protocol_value.hpp"

#include "protocol_parser.hpp"

namespace protocol
{
	int ProtocolParser::encode(ListBuffer & listbuffer, void * data)
	{
		Protocol & protocol = *(Protocol*)data;

		Streamer streamer(Streamer::ISTREAMER, listbuffer);

		uint32_t type = protocol.protocol_type();

#define USE_SWITCH

#define PROTOCOL_CMD(class, cmd0, cmd1, cmd1_value) \
		case MakeUint32_t<cmd0, cmd1>::value: \
		{ 	\
			streamer & (*(class*)&protocol); \
			\
			return 0; \
		} \
		break;

		
#include "protocol_value.def"

#undef PROTOCOL_CMD

#undef USE_SWITCH

		return -1;
	}


#define PROTOCOL_HEADER_VALUE(pos, type) \
	{ \
		if ((uint32_t)listbuffer.bytes_consumable() < Protocol::LENGTH) \
		{ \
			return (type)-1; \
		} \
	\
		type value = 0; \
		listbuffer.copy_out(value, pos); \
		return value; \
	}

	int ProtocolParser::decode(ListBuffer & listbuffer, void *& data)
	{
		if (!frame(listbuffer))
		{
			return 0;
		}

		Streamer streamer(Streamer::OSTREAMER, listbuffer);
	
		uint16_t cmd0 = get_cmd0(listbuffer);
		uint16_t cmd1 = get_cmd1(listbuffer);
		
		uint32_t type = (((uint32_t)cmd0) << 16) | ((uint32_t)cmd1);

#define USE_SWITCH

#define PROTOCOL_CMD(class, cmd0, cmd1, cmd1_value) \
		case MakeUint32_t<cmd0, cmd1>::value: \
		{ \
			class * protocol  = new (_buffer) class; \
		\
			streamer & (*(class*)protocol); \
		\
			data             = protocol; \
		\
			return 0; \
		}

#include "protocol_value.def"

#undef PROTOCOL_CMD

#undef USE_SWITCH

		return -1;
	}
	
	bool ProtocolParser::frame(ListBuffer & listbuffer)
	{
		return (get_length(listbuffer) <= (uint32_t)listbuffer.bytes_consumable());
	}
	
	uint32_t ProtocolParser::get_magic(ListBuffer & listbuffer)
	{
		PROTOCOL_HEADER_VALUE(Protocol::MAGIC_POS, uint32_t);
	}

	uint32_t ProtocolParser::get_length(ListBuffer & listbuffer)
	{
		PROTOCOL_HEADER_VALUE(Protocol::LENGTH_POS, uint32_t);
	}

	uint16_t ProtocolParser::get_cmd0(ListBuffer & listbuffer)
	{
		PROTOCOL_HEADER_VALUE(Protocol::CMD0_POS, uint16_t);
	}

	uint16_t ProtocolParser::get_cmd1(ListBuffer & listbuffer)
	{
		PROTOCOL_HEADER_VALUE(Protocol::CMD1_POS, uint16_t);
	}

#undef PROTOCOL_HEADER_VALUE
};

